use std::collections::HashMap;

use report::{ChartReport, CrossReport, ListReport, ReportTrait};
use serde_json::Value;
use shq_common::prelude::{
    report::{DisplayCols, ReportDataResponse, ReportDefine}, DataType, Operator, OrderItems, WhereCauses
};
mod config;
mod database;
mod error;
mod export;
mod func;
mod report;
pub use config::*;
use error::{Error, Result};
pub struct ReportService<'a> {
    pub name: String,
    pub user_id: u32,
    pub menu_id: u32,
    pub rept: Box<dyn ReportTrait + 'a>,
}
impl<'a> ReportService<'a> {
    pub async fn new(
        name: &'a str,
        params: &'a [(&'a str, &'a str)],
        user_id: u32,
        menu_id: u32,
    ) -> Result<Self> {
        let rept_conf = get_rept_config(name).ok_or(Error::from_conf(name, "报表不存在"))?;
        let dvs = rept_conf.get_default_param_vals().await.or_else(|e| {
            Err(Error::from_create(name, format!("获取默认参数失败: {}", e)))
        })?;
        let params = dvs
            .into_iter()
            .map(|(k, v)| {
                params
                    .iter()
                    .find(|(k1, _)| *k1 == k)
                    .map_or_else(|| (k, v), |(k, v)| (k, v.to_string()))
            })
            .collect::<Vec<(&str, String)>>();
        match rept_conf.r#type {
            config::ReptType::List => {
                let rept = ListReport::from(name, params, user_id, menu_id)
                    .await
                    .or_else(|e| {
                        log::error!("ListReport error: {:?}", e);
                        Err(Error::from_create(name, "创建报表失败"))
                    })?;
                Ok(ReportService {
                    name: name.to_string(),
                    rept: Box::new(rept),
                    user_id,
                    menu_id,
                })
            }
            config::ReptType::Chart => {
                let rept = ChartReport::from(name, params, user_id, menu_id).await?;
                Ok(ReportService {
                    name: name.to_string(),
                    rept: Box::new(rept),
                    user_id,
                    menu_id,
                })
            }
            config::ReptType::Cross => {
                let rept = CrossReport::from(name, params, user_id, menu_id).await?;
                Ok(ReportService {
                    name: name.to_string(),
                    rept: Box::new(rept),
                    user_id,
                    menu_id,
                })
            }
        }
    }
    fn convert_where_causes(&self, mut wcs: WhereCauses) -> anyhow::Result<WhereCauses> {
        let conf = get_rept_config(self.name.as_str())
            .ok_or_else(|| Error::from_conf(self.name.as_str(), "报表配置不存在"))?;
        for wc in wcs.as_mut() {
            if let Some(col) = conf
                .columns
                .iter()
                .find(|c| c.name.as_deref() == Some(wc.field_name.as_str()))
            {
                if col.data_type == DataType::Int || col.data_type == DataType::Float {
                    wc.filter_items.iter_mut().for_each(|fi| {
                        if fi.operator==Operator::In || fi.operator==Operator::NotIn {
                            // 如果是in操作符且没有值，则不处理
                            return;
                        }
                        if let Some(val) = &fi.value {
                            let is_perc = col
                                .format
                                .as_ref()
                                .map(|fmt| fmt.ends_with("%"))
                                .unwrap_or(false);
                            let unit = col.unit.map(|u| u.to_u32()).unwrap_or(1) as f64;
                            let mut fv = val.parse::<f64>().unwrap_or(0.0);
                            if is_perc {
                                fv = fv / 100.0;
                            } else {
                                fv = fv / unit;
                            }
                            if col.data_type == DataType::Int {
                                let iv = fv as i64;
                                fi.value = Some(iv.to_string());
                            } else if col.data_type == DataType::Float {
                                fi.value = Some(fv.to_string());
                            }
                        }
                    });
                }
            }
        }
        Ok(wcs)
    }
    pub async fn get_rept_define(&self) -> Result<ReportDefine> {
        self.rept.get_rept_define().await
    }
    pub async fn get_options_map(&self) -> Result<HashMap<String, Vec<String>>> {
        self.rept.get_options_map().await
    }
    pub async fn get_data_list(
        &self,
        where_causes: Option<WhereCauses>,
        orders: Option<OrderItems>,
        display_cols: DisplayCols,
        group_index: u8,
        drill_down_requ: Vec<(u8, Value)>,
        extra_where: Option<String>,
        page: u32,
        size: u16,
    ) -> Result<ReportDataResponse> {
        let where_causes=if let Some(wcs) = where_causes {
            let wcs=self.convert_where_causes(wcs)
                .map_err(|e| Error::from_conf(self.name.as_str(), e.to_string()))?;
            Some(wcs)
        }else{
            None
        };
        self.rept
            .get_data_list(
                where_causes,
                orders,
                display_cols,
                group_index,
                drill_down_requ,
                extra_where,
                page,
                size,
            )
            .await
    }
    pub async fn export_xslx(
        &self,
        where_causes: Option<WhereCauses>,
        orders: Option<OrderItems>,
        display_cols: DisplayCols,
        group_index: u8,
        drill_down_requ: Vec<(u8, Value)>,
        extra_where: Option<String>,
        one_sheet_on_file: bool,
    ) -> Result<Vec<u8>> {
        self.rept
            .export_xslx(
                where_causes,
                orders,
                display_cols,
                group_index,
                drill_down_requ,
                extra_where,
                one_sheet_on_file
            )
            .await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use shq_common::prelude::report::DisplayCols;
    use tracing_subscriber::{filter::LevelFilter, prelude::*};
    #[tokio::test]
    async fn test1() {
        dotenv::dotenv().ok();
        let stdout_log = tracing_subscriber::fmt::layer().pretty();
        tracing_subscriber::registry()
            .with(LevelFilter::INFO)
            .with(stdout_log)
            .init();
        shq_common::prelude::init_app_config().await.unwrap();
        init_rept_config().await.unwrap();
        let params: Vec<(&str, &str)> = Vec::new();
        // params.push(("sdate", "20220101"));
        // params.push(("time_level", "quarter"));
        // params.push(("ny", "202501"));
        // params.push(("nian", "2025"));
        // params.push(("empl_id", "all"));
        // params.push(("item_type", "TPNO"));
        // params.push(("empl_level", "all"));
        //mx_dg_ck,ywbb_zh_bmcdktj,analysis_comp_clie,ywfx_eva_zbcb,ywfx_qxfx_ckacqtj,chart_bar_line,chart_pie
        let rept = ReportService::new("chart_typm_lines", params.as_slice(), 0, 903)
            .await
            .unwrap();
        let def = rept.get_rept_define().await.unwrap();
        log::info!("def:{:?}", def.groups);
        // [("sdate", "20240101"), ("edate", "20240103"), ("cyno", "1"), ("data_level", "USER"), ("empl_level", "USER"), ("empl_id", "0"), ("cuid", "0"), ("item_type", "TPNO"), ("col_name", "ACBL_DGCK")]
        // let display_cols = DisplayCols::Part(vec!["c_lrzb_1_3".to_owned(), "dwmc".to_owned(), "c_lrzb_1_2".to_owned(), "c_lrzb_1_1".to_owned()]);
        let res = rept
            .get_data_list(
                None,
                None,
                DisplayCols::Default,
                0,
                vec![], //vec![(1,31001.into())],
                None,
                1,
                1000,
            )
            .await;
        match res {
            Ok(res) => {
                log::info!("res:{:?}", res.total);
            }
            Err(e) => {
                log::error!("err:{:?}", e);
            }
        }
    }
}
